package phase4Specification.testing;

import java.util.ArrayList;
import java.util.Iterator;

import dbPhase.hypeerweb.HyPeerWeb;
import dbPhase.hypeerweb.HyPeerWebDatabase;
import dbPhase.hypeerweb.Node;
import dbPhase.hypeerweb.Parameters;
import dbPhase.hypeerweb.SendVisitor;
import dbPhase.hypeerweb.SimplifiedNodeDomain;

public class Exp {
	public static HyPeerWeb hypeerweb = null;
	private static int broadcastTestCount = 528;
	private static int errorCount = 0;

	public static void main(String[] args) {
		HyPeerWebDatabase.initHyPeerWebDatabase();
		HyPeerWebDatabase.getSingleton().clear();
		hypeerweb = HyPeerWeb.getSingleton();
		testPhase4();
	}

	static private void createHyPeerWebWith(int numberOfNodes) {
		hypeerweb.clear();
		Node node0 = new Node(0);
		hypeerweb.addToHyPeerWeb(node0, null);

		for (int i = 1; i < numberOfNodes; i++) {
			Node node = new Node(0);
			node0.addToHyPeerWeb(node);
		}
	}
	
	private static void testPhase4() {
    	System.out.println("Testing Phase 4");
		testSendVisitor();
		testBroadcastVisitor();
	}
	
	private static boolean sendVisitorErrorHeaderPrinted = false;

	public static void testSendVisitor() {
		sendVisitorErrorHeaderPrinted = false;
		boolean error = false;
		errorCount = 0;
		System.out.print("    ");
		for (int i = 0; i < sizes.length; i++) {
			int hypeerwebSize = sizes[i];
			int target = targets[i];
        	if(VERBOSE) {
        		System.out.println("Testing Send Visitor on HyPeerWeb of size " + hypeerwebSize + ", starting from node " + i + " and going to node " + target);
        	} else {
        		System.out.print(i + " ");
        	}
			boolean testSendError = testSend(hypeerwebSize, target);
			if(testSendError){
				errorCount++;
				error = true;
			}
		}
        if(!VERBOSE){
        	System.out.println();
        }
        
        if (error) {
    		double decimalPercent = ((double)sendTestCount - (double) errorCount) / (double) sendTestCount;
    		int integerPercent = (int) (decimalPercent * 100.0d);
    		System.out.println("    Send Visitor Score = " + integerPercent + "%");
        } else {
            System.out.println("    No Send Visitor Errors: Phase 4 Score, part 1 = 100%");
        }
	}

	@SuppressWarnings("unchecked")
	private static boolean testSend(int hypeerwebSize, int target) {
		createHyPeerWebWith(hypeerwebSize);
		
		for (int i = 0; i < hypeerwebSize; i++) {
			TrackSendPathVisitor trackSendPathVisitor = new TrackSendPathVisitor();
			Parameters parameters = new Parameters();
			parameters.set(SendVisitor.TARGET_KEY, target);
			ArrayList<Integer> emptyList = new ArrayList<Integer>();
			parameters.set(TrackSendPathVisitor.PATH_KEY, emptyList);
			hypeerweb.getNode(i).accept(trackSendPathVisitor, parameters);
		}
		ArrayList<ArrayList<Integer>> finalResult = (ArrayList<ArrayList<Integer>>) hypeerweb.getNode(target).getContents().get(TrackSendPathVisitor.PATHS_KEY);
		return checkFinalResult(finalResult, target);
	}
	
	private static boolean checkFinalResult(ArrayList<ArrayList<Integer>> paths, int target) {
		boolean result = false;
		for (ArrayList<Integer> path : paths) {
			int loc = checkFinalResultz(path, target);
			if (loc != -1) {
				result = true;
				if (!sendVisitorErrorHeaderPrinted) {
					System.out.println("-------------------------------------------------------------");
					System.out.println("Errors encountered while testing the SendVisitor");
				}
				sendVisitorErrorHeaderPrinted = false;
				System.out.print("    For the path: ");
				for (Integer i : path) {
					System.out.print(i + " ");
				}
				System.out.println(" there was a better choice than going from node "	+ path.get(loc) + " to " + path.get(loc + 1));
				System.out.println("    This was the first incorrect choice detected, there may be others");
			}
		}
		return result;
	}

	private static int checkFinalResultz(ArrayList<Integer> path, int targetId) {
		int result = -1;
		for (int i = 0; result == -1 && i < path.size() - 1; i++) {
			///System.out.print(result);
			if (!isBestChoice(path.get(i), path.get(i + 1), targetId)) {
				result = i;
			}
		}
		return result;
	}

	private static boolean isBestChoice(int source, int nextNode, int targetId) {
		SimplifiedNodeDomain sourceNode = hypeerweb.getNode(source).constructSimplifiedNodeDomain();
		boolean result = !sourceNode.containsCloserNode(nextNode, targetId);
		return result;
	}
	
	private static void testBroadcastVisitor() {
		boolean error = false;
		errorCount = 0;
		System.out.print("\n    ");
		for (int hypeerwebSize = 1; hypeerwebSize <= HYPEERWEB_SIZE; hypeerwebSize++) {
			createHyPeerWebWith(hypeerwebSize);
        	if(VERBOSE) {
    			System.out.println("Testing Broadcast on HyPeerWeb of size " + hypeerwebSize);
        	} else {
        		System.out.print(hypeerwebSize + " ");
        	}
			for (int i = 0; i < hypeerwebSize; i++) {
				DepositBroadcastVisitor depositBroadcastVisitor = new DepositBroadcastVisitor();
				Parameters parameters = new Parameters();
				parameters.set(DepositBroadcastVisitor.DEPOSIT_KEY, i);
				hypeerweb.getNode(i).accept(depositBroadcastVisitor, parameters);
				ArrayList<Integer> nodesNotVisited = checkNodes(i);
				if (!nodesNotVisited.isEmpty()) {
					error = true;
					errorCount++;
					
					System.out.println("Error when broadcasting from node " + i	+ " to a HyPeerWeb of size " + hypeerwebSize);
					System.out.print("    Nodes not reached were: ");
					for (int node : nodesNotVisited) {
						System.out.print(node + " ");
					}
					System.out.println();
				}
			}
		}
		
        if(!VERBOSE){
        	System.out.println();
        }
        
        if (error) {
    		double decimalPercent = ((double)broadcastTestCount - (double) errorCount) / (double) broadcastTestCount;
    		int integerPercent = (int) (decimalPercent * 100.0d);
    		System.out.println("    Broadcast Score = " + integerPercent + "%");
        } else {
            System.out.println("    No Broadcast Errors: Phase 4 Score, part 2 = 100%");
        }
	}

	private static ArrayList<Integer> checkNodes(int i) {
		ArrayList<Integer> nodesNotVisited = new ArrayList<Integer>();

		int index = 0;
		Iterator<Node> iter = hypeerweb.iterator();
		while(iter.hasNext()){
			Node node = iter.next();
			if ((Integer) node.getContents().get(DepositBroadcastVisitor.DEPOSIT_KEY) != i) {
				nodesNotVisited.add(index);
			}
			index++;
		}

		return nodesNotVisited;
	}

	private static int[] sizes = 
		{  1,  2,  2,  3,  3,  3,  4,  4,  4,  4,
		  10, 53, 39, 46, 67, 75, 25, 85, 43, 86,
		  61,127, 75, 84, 34, 21,113,  7, 77, 65,
		  70, 41, 69,114,128, 91, 96, 38, 31, 48,
		  21,113,101,100, 42, 21, 47, 71,100,114};
	
	private static int sendTestCount = sizes.length;

	private static int[] targets =
		{  0,  0,  1,  0,  1,  2,  0,  1,  2,  3,
		   4, 14, 15,  1, 60, 50,  5, 51, 35, 71,
		  41,121, 58,  5,  1,  0,108,  1, 74, 60,
		   4, 20, 44, 63, 54, 70, 27, 27,  4, 14,
		   9, 46, 66, 12, 17, 13, 18, 39, 44,107};
		
	private static final int HYPEERWEB_SIZE = 32;
	private static final boolean VERBOSE = false;
}